﻿using System;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using FamilyBook.Core.Entities;

namespace FamilyBook.Repository.EF
{
    public sealed class FamilyBookDbContext : DbContext
    {
        private const string DbScheme = "fb";

        public FamilyBookDbContext(string nameOrConnectionString)
            :base(nameOrConnectionString)
        {
            //this.Configuration.LazyLoadingEnabled = false;
            //this.Configuration.ValidateOnSaveEnabled = false;
            //this.Configuration.ProxyCreationEnabled = false;
            //this.Configuration.AutoDetectChangesEnabled = false;

            //Database.SetInitializer(new CreateDatabaseIfNotExists<FamilyBookDbContext>());
            Database.SetInitializer<FamilyBookDbContext>(null);
        }

        //public DbSet<Audit> Audits { get; set; }
        public DbSet<User> Users { get; set; }
        public DbSet<Counterparty> Counterparties { get; set; }
        public DbSet<Account> Accounts { get; set; }
        public DbSet<Currency> Currencies { get; set; }
        public DbSet<Product> Products { get; set; }
        public DbSet<Transaction> Transactions { get; set; }
        public DbSet<Sequence> Sequences { get; set; }

        /*
        public ObjectContext ObjectContext
        {
            get { return ((IObjectContextAdapter) this).ObjectContext; }
        }
        */

        public override int SaveChanges()
        {
            foreach (DbEntityEntry dbEntityEntry in ChangeTracker.Entries().Where(x => x.State == EntityState.Added))
            {
                //IEntityVersionable
                var entityVersionable = dbEntityEntry.Entity as IEntityVersionable;
                if (entityVersionable != null)
                {
                    entityVersionable.Version = 1;
                    entityVersionable.CreatedDate = DateTime.Now;
                    entityVersionable.UpdatedDate = entityVersionable.CreatedDate;
                }
            }
            foreach (DbEntityEntry dbEntityEntry in ChangeTracker.Entries().Where(x => x.State == EntityState.Modified))
            {
                var entityVersionable = dbEntityEntry.Entity as IEntityVersionable;
                if (entityVersionable != null)
                {
                    entityVersionable.Version++;
                    entityVersionable.UpdatedDate = DateTime.Now;
                }
            }

            return base.SaveChanges();
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            //modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();

            //modelBuilder.Entity<Audit>().ToTable("Audit");
            //modelBuilder.Entity<Audit>().HasKey(x => x.AuditId);

            //Sequence
            modelBuilder.Entity<Sequence>().ToTable("Sequence", DbScheme);
            modelBuilder.Entity<Sequence>().Property(x => x.Version).IsConcurrencyToken();

            //Currency
            modelBuilder.Entity<Currency>().ToTable("Currency", DbScheme);
            modelBuilder.Entity<Currency>().HasKey(x => x.Code).Property(x => x.Code).HasMaxLength(3);
            modelBuilder.Entity<Currency>().Property(x => x.Name).IsRequired().HasMaxLength(200);

            //Account
            //modelBuilder.Configurations.Add(new AccountConfiguration());
            modelBuilder.Entity<Account>().ToTable("Account", DbScheme);
            modelBuilder.Entity<Account>().Property(x => x.Number).IsRequired().HasMaxLength(10);
            modelBuilder.Entity<Account>().Property(x => x.DisplayedName).HasMaxLength(100);
            modelBuilder.Entity<Account>().Property(x => x.Version).IsConcurrencyToken();
            modelBuilder.Entity<Account>().HasRequired(x => x.Currency).WithMany().Map(x => x.MapKey("CurrencyCode")).WillCascadeOnDelete(false);
            //modelBuilder.Entity<Account>().HasRequired(x => x.Custodian).WithMany(x => x.Accounts).WillCascadeOnDelete(false);
            
            //Counterparty
            modelBuilder.Entity<Counterparty>().ToTable("Counterparty", DbScheme);
            modelBuilder.Entity<Counterparty>().Property(x => x.Version).IsConcurrencyToken();
            modelBuilder.Entity<Counterparty>().HasMany(x => x.Accounts).WithRequired(x => x.Custodian)
                .Map(x => x.MapKey("CounterpartyId")).WillCascadeOnDelete(false);

            //Product
            modelBuilder.Entity<Product>().ToTable("Product", DbScheme);
            modelBuilder.Entity<Product>().Property(x => x.Name).IsRequired().HasMaxLength(200);
            modelBuilder.Entity<Product>().Property(x => x.Version).IsConcurrencyToken();
            modelBuilder.Entity<Product>().HasOptional(x => x.Parent).WithMany(x => x.ChildProducts).Map(x => x.MapKey("ParentId"));
            modelBuilder.Entity<Product>().HasOptional(x => x.Counterparty).WithMany(x => x.Products).Map(x => x.MapKey("CounterpartyId"));

            //Transaction
            modelBuilder.Entity<Transaction>().ToTable("Transaction", DbScheme);
            modelBuilder.Entity<Transaction>().Property(x => x.Version).IsConcurrencyToken();
            modelBuilder.Entity<Transaction>().HasRequired(x => x.SrcAccount).WithMany(x => x.OutgoingTransactions)
                .Map(x => x.MapKey("SrcAccountId")).WillCascadeOnDelete(false);
            modelBuilder.Entity<Transaction>().HasRequired(x => x.DstAccount).WithMany(x => x.IncomingTransactions)
                .Map(x => x.MapKey("DstAccountId")).WillCascadeOnDelete(false);
            modelBuilder.Entity<Transaction>().HasOptional(x => x.Product).WithMany().Map(x => x.MapKey("ProductId"));

            //ContractAccount
            modelBuilder.Entity<ContractAccount>().ToTable("ContractAccount", DbScheme);
            modelBuilder.Entity<ContractAccount>().HasRequired(x => x.Contractor).WithMany()
                .Map(x => x.MapKey("CounterpartyId")).WillCascadeOnDelete(false);
            modelBuilder.Entity<ContractAccount>().HasOptional(x => x.Product).WithMany().Map(x => x.MapKey("ProductId"));

            //Person
            modelBuilder.Entity<Person>().ToTable("Person", DbScheme);
            //modelBuilder.Entity<Person>().Property(x => x.CounterpartyId).HasColumnName("PersonId");
            modelBuilder.Entity<Person>().Property(x => x.FirstName).IsRequired().HasMaxLength(200);
            modelBuilder.Entity<Person>().Property(x => x.LastName).HasMaxLength(200);

            //Organization
            modelBuilder.Entity<Organization>().ToTable("Organization", DbScheme);
            //modelBuilder.Entity<Organization>().Property(x => x.CounterpartyId).HasColumnName("OrganizationId");
            modelBuilder.Entity<Organization>().Property(x => x.Name).IsRequired().HasMaxLength(200);
            modelBuilder.Entity<Organization>().HasMany(x => x.Persons).WithMany(x => x.Organizations)
                .Map(x =>
                {
                    x.MapLeftKey("OrganizationId");
                    x.MapRightKey("PersonId");
                    x.ToTable("OrganizationPerson", DbScheme);
                });

            //User
            modelBuilder.Entity<User>().ToTable("User", DbScheme);
            modelBuilder.Entity<User>().Property(x => x.Version).IsConcurrencyToken();
            modelBuilder.Entity<User>().HasOptional(x => x.Owner).WithMany().Map(x => x.MapKey("CounterpartyId"));
            modelBuilder.Entity<User>().Property(x => x.Name).HasMaxLength(200).IsRequired();
            modelBuilder.Entity<User>().Property(x => x.Password).HasMaxLength(200).IsRequired();

            //CurrencyExchange
            modelBuilder.Entity<CurrencyExchange>().ToTable("CurrencyExchange", DbScheme);
            modelBuilder.Entity<CurrencyExchange>().HasRequired(x => x.BaseCurrency).WithMany().Map(x => x.MapKey("BaseCurrencyCode")).WillCascadeOnDelete(false);
            modelBuilder.Entity<CurrencyExchange>().HasRequired(x => x.QuoteCurrency).WithMany().Map(x => x.MapKey("QuoteCurrencyCode")).WillCascadeOnDelete(false);
            modelBuilder.Entity<CurrencyExchange>().HasRequired(x => x.BuyTransaction).WithMany().Map(x => x.MapKey("BuyTransactionId")).WillCascadeOnDelete(false);
            modelBuilder.Entity<CurrencyExchange>().HasRequired(x => x.SellTransaction).WithMany().Map(x => x.MapKey("SellTransactionId")).WillCascadeOnDelete(false);
            
            base.OnModelCreating(modelBuilder);
        }

    }

    /*
    public class AccountConfiguration : EntityTypeConfiguration<Account>
    {
        public AccountConfiguration()
            :base()
        {
            
        }
    }
    */
}
